package com.um.springboot.starter.interceptors;

import com.um.springboot.starter.annotation.ActionLogRequired;
import com.um.springboot.starter.code.CommonCode;
import com.um.springboot.starter.constants.Constants;
import com.um.springboot.starter.exception.ServiceException;
import com.um.springboot.starter.helper.SessionHelper;
import com.um.springboot.starter.orm.entity.Response;
import com.um.springboot.starter.properties.HshProperties;
import com.um.springboot.starter.service.CommonService;
import com.um.springboot.starter.utils.JSONUtil;
import com.um.springboot.starter.utils.NetUtil;
import com.um.springboot.starter.utils.SpringAsyncUtil;
import com.um.springboot.starter.utils.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * @author 邓南辉
 * 日期 2019/12/13
 * 版权所有 广州优匠科技有限公司
 */
@Slf4j
@Aspect     //@Aspect注解就是告诉spring 这是一个aop类,AOP切面
@Component   //可理解为用spring的时候xml里面的<beans>标签,类中 @Bean可以理解为用Spring的时候xml里面的<bean>标签
@Order(value = AspectOrders.ACTION_LOG_ORDER)
public class ActionLogRequiredInterceptor {

    @Autowired
    HshProperties hshProperties;

    @Autowired
    HttpServletRequest request;

    @Autowired
    CommonService commonService;

    @Autowired
    SessionHelper sessionHelper;

    @Autowired
    SpringAsyncUtil springAsyncUtil;

    @Value("${spring.application.name:}")
    String applicationName;

    //@Around注解 环绕执行，就是在调用之前和调用之后，都会执行一定的逻辑
    @Around("execution(* com.um..controller.*.*(..)) || execution(* com.hsh..controller.*.*(..))")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        long beginTime = System.currentTimeMillis();

        String allowsIp = hshProperties.getConfig().getAllowsIp();
        if (!allowsIp.equals("*")) {
            String ip = NetUtil.getRealIP(request);
            if (allowsIp.indexOf(ip) <= 0) {
                return Response.failed(CommonCode.NoApiPermission);
            }
        }

        MethodSignature signature = (MethodSignature) pjp.getSignature();
        ActionLogRequired actionLogRequired = signature.getMethod().getAnnotation(ActionLogRequired.class);
        //如果从方法拿不到，则从当前类中拿是否有注解
        if (actionLogRequired == null)
            actionLogRequired = pjp.getTarget().getClass().getAnnotation(ActionLogRequired.class);

        if (actionLogRequired != null && actionLogRequired.ignore()) {
            return pjp.proceed();
        }

        //记录请求参数，用于在错误日志中保存
        Object[] args = Arrays.stream(pjp.getArgs())
                .filter(arg -> !(arg instanceof HttpServletRequest || arg instanceof HttpServletResponse ||
                        arg instanceof Model || arg instanceof MultipartFile))
                .toArray();
        String parameters = "[]";
        try {
            parameters = JSONUtil.toJSONString(args);
        } catch (Exception ignored) {
        }
        request.setAttribute("parameters", parameters);

        //添加每个操作唯一标识
        if (request.getAttribute(Constants.EXECUTE_ID) == null) {
            request.setAttribute(Constants.EXECUTE_ID, UUID.randomUUID().toString().replaceAll("-", ""));
        }

        ServiceException exception = null;
        Object result = null;
        try {
            result = pjp.proceed();
        } catch (ServiceException ex) {
            exception = ex;
        }

        //记录操作日志运行时长
        if (hshProperties.getConfig().isHistoryLog()) {
            try {
                String uri = request.getRequestURI();
                String appName = StringUtil.isEmpty(applicationName) ? "" : applicationName + ":";
                Object returnData = exception == null ? result : Response.failed(exception.getErrCode(), exception.getMsg(), exception.getData());
                long executeTime = System.currentTimeMillis() - beginTime;

                Map<String, Object> actionLog = new Hashtable<>();
                actionLog.put("action_name", appName + uri.substring(0, uri.lastIndexOf("/")));
                actionLog.put("method_name", uri.substring(uri.lastIndexOf("/")));
                actionLog.put("parameters", parameters);
                actionLog.put("return_data", "[]");
                actionLog.put("create_date", new Date());

                actionLog.put("ip_address", NetUtil.getRealIP(request));
                actionLog.put("app_flag", hshProperties.getConfig().getAppFlag());

                Map sessionUser = sessionHelper.getSessionUser();
                if (sessionUser != null && sessionUser.get(hshProperties.getConfig().getSessionUserNoField()) != null) {
                    actionLog.put("user_no", sessionUser.get(hshProperties.getConfig().getSessionUserNoField()));
                }

                actionLog.put("execute_time", executeTime);
                actionLog.put("operator", "1");
                actionLog.put("return_data", JSONUtil.toJSONString(returnData));

                springAsyncUtil.run(() -> commonService.saveRecord("sys_action_log", actionLog));
            } catch (Exception ex) {
                log.error("", ex);
            }
        }

        if (exception != null) {
            throw exception;
        }
        return result;
    }

}
